package com.fary.net;

import com.fary.util.SynchronizedQueue;

import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicInteger;

public class NioBlockingSelector {

    private static AtomicInteger threadCounter = new AtomicInteger(0);

    protected Selector sharedSelector;

    protected BlockPoller poller;

    public void open(Selector selector) {
        sharedSelector = selector;
        poller = new BlockPoller();
        poller.selector = sharedSelector;
        poller.setDaemon(true);
        poller.setName("NioBlockingSelector.BlockPoller-" + (threadCounter.getAndIncrement()));
        poller.start();
    }

    protected static class BlockPoller extends Thread {
        protected volatile boolean run = true;
        protected Selector selector = null;
        protected final SynchronizedQueue<Runnable> events = new SynchronizedQueue<>();
        // 唤醒线程计数
        protected final AtomicInteger wakeupCounter = new AtomicInteger(0);

        @Override
        public void run() {
            while (run) {
                try {
                    events();
                    int keyCount = 0;
                    try {
                        if (wakeupCounter.getAndSet(-1) > 0) {
                            keyCount = selector.selectNow();
                        } else {
                            keyCount = selector.select(1000);
                        }
                        wakeupCounter.set(0);
                        if (!run) {
                            break;
                        }
                    } catch (NullPointerException x) {
                        if (selector == null) {
                            throw x;
                        }
                        continue;
                    } catch (Throwable x) {
                        continue;
                    }

                    Iterator<SelectionKey> iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;

                    // Walk through the collection of ready keys and dispatch
                    // any active event.
                    while (run && iterator != null && iterator.hasNext()) {
                        SelectionKey sk = iterator.next();
//                        NioSocketWrapper attachment = (NioSocketWrapper) sk.attachment();
//                        try {
//                            iterator.remove();
//                            sk.interestOps(sk.interestOps() & (~sk.readyOps()));
//                            if (sk.isReadable()) {
//                                countDown(attachment.getReadLatch());
//                            }
//                            if (sk.isWritable()) {
//                                countDown(attachment.getWriteLatch());
//                            }
//                        } catch (CancelledKeyException ckx) {
//                            sk.cancel();
//                            countDown(attachment.getReadLatch());
//                            countDown(attachment.getWriteLatch());
//                        }
                    }//while
                } catch (Throwable ignored) {

                }
            }
            events.clear();
            // If using a shared selector, the NioSelectorPool will also try and
            // close the selector. Try and avoid the ClosedSelectorException
            // although because multiple threads are involved there is always
            // the possibility of an Exception here.
            if (selector.isOpen()) {
                try {
                    // Cancels all remaining keys
                    selector.selectNow();
                } catch (Exception ignore) {

                }
            }
            try {
                selector.close();
            } catch (Exception ignore) {

            }
        }

        public boolean events() {
            Runnable r;
            int size = events.size();
            for (int i = 0; i < size && (r = events.poll()) != null; i++) {
                r.run();
            }

            return (size > 0);
        }

        public void wakeup() {
            if (wakeupCounter.addAndGet(1) == 0) {
                selector.wakeup();
            }
        }
    }
}